package com.altekis.kanbankaze.server.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

/**
 * JDO implementation of the {@link GenericDAO}.
 * 
 * @author gabrielp
 * 
 * @param <T>
 *            The persistent type
 * @param <ID>
 *            The primary key type
 */
public class GenericDAOJDOImpl<T, ID extends Serializable> implements
		GenericDAO<T, ID> {

	// ~ Instance fields
	// --------------------------------------------------------

	private final Class<T> persistentClass;
	private static final PersistenceManagerFactory PMF = JDOHelper
			.getPersistenceManagerFactory("transactions-optional");

	// ~ Constructors
	// -----------------------------------------------------------

	@SuppressWarnings("unchecked")
	public GenericDAOJDOImpl() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public GenericDAOJDOImpl(final Class<T> persistentClass) {
		super();
		this.persistentClass = persistentClass;
	}

	// ~ Methods
	// ----------------------------------------------------------------

	@Override
	public void delete(T object) {
		PersistenceManager pm = getPersistenceManager();
		try {
			pm.deletePersistent(object);
		} finally {
			pm.close();
		}
	}

	@Override
	public List<T> findAll() {
		PersistenceManager pm = getPersistenceManager();
		try {
			Query q = pm.newQuery(persistentClass);
			@SuppressWarnings("unchecked")
			List<T> fetched = (List<T>) q.execute();
			return (List<T>) pm.detachCopyAll(fetched);
		} finally {
			pm.close();
		}

	}

	@Override
	public T findById(final ID id) {
		PersistenceManager pm = getPersistenceManager();
		try {
			T fetched = (T) pm.getObjectById(persistentClass, id);
			return pm.detachCopy(fetched);
		} finally {
			pm.close();
		}

	}

	/**
	 * Find using a named query.
	 * 
	 * @param queryName
	 *            the name of the query
	 * @param params
	 *            the query parameters
	 * 
	 * @return the list of entities
	 */
	protected List<T> findByNamedQuery(String queryName, Object... params) {
		PersistenceManager pm = getPersistenceManager();
		try {
			Query q = pm.newNamedQuery(persistentClass, queryName);
			@SuppressWarnings("unchecked")
			List<T> fetched = (List<T>) q.executeWithArray(params);
			return (List<T>) pm.detachCopyAll(fetched);
		} finally {
			pm.close();
		}
	}

	/**
	 * Find using a named query.
	 * 
	 * @param queryName
	 *            the name of the query
	 * @param params
	 *            the query parameters
	 * 
	 * @return the list of entities
	 */
	protected List<T> findByNamedQueryAndNamedParams(String queryName,
			Map<String, ? extends Object> params) {
		PersistenceManager pm = getPersistenceManager();
		try {
			Query q = pm.newNamedQuery(persistentClass, queryName);
			@SuppressWarnings("unchecked")
			List<T> fetched = (List<T>) q.executeWithMap(params);
			return (List<T>) pm.detachCopyAll(fetched);
		} finally {
			pm.close();
		}

	}

	@Override
	public void flush() {
		PersistenceManager pm = getPersistenceManager();
		try {
			pm.flush();
		} finally {
			pm.close();
		}
	}

	@Override
	public Class<T> getEntityClass() {
		return persistentClass;
	}

	public PersistenceManager getPersistenceManager() {
		return PMF.getPersistenceManager();
	}

	@Override
	public T save(T object) {
		PersistenceManager pm = getPersistenceManager();
		try {
			return pm.makePersistent(object);
		} finally {
			pm.close();
		}
	}
}
